// $Id: proxy.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#include <xpcom/config.hpp>
#include <cstring> // for std::memcpy
#include <exception>
#include <xpcom/assert.hpp>
#include <xpcom/char_traits.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/string.hpp>
#include <xpcom/uuid.hpp>
#include <xpcom/proxy/pstring.hpp>
#include <xpcom/proxy/pstring_ref.hpp>
#include <xpcom/proxy/uuid_ptr.hpp>

namespace xpcom {
namespace proxy {

// pstring_ref
template <typename CharT>
pstring_ref<CharT>::~pstring_ref()
{
    if (pstr_)
        memory::free(pstr_);
}

template <typename CharT>
void pstring_ref<CharT>::init(const value_type* s, size_type n)
{
    if (s) {
        if (n == static_cast<size_type>(-1))
            n = static_cast<size_type>(char_traits<value_type>::length(s));

        pstr_ = memory::alloc<value_type>(n + 1);
        std::memcpy(pstr_, s, n * sizeof(value_type));
        pstr_[n] = value_type(0);

    } else {
        pstr_ = 0;
    }
}

template <typename CharT>
void pstring_ref<CharT>::init(const basic_string<value_type>& s)
{
    pstr_ = s.clone_data();
}

template <typename CharT>
bool pstring_ref<CharT>::equal(const value_type* s, size_type n) const
{
    // At least one of the pointer is null
    if (!(pstr_ && s))
        return pstr_ == s;

    size_type n1 =
            static_cast<size_type>(char_traits<value_type>::length(pstr_));
    if (n == static_cast<size_type>(-1))
        n = static_cast<size_type>(char_traits<value_type>::length(s));

    return n1 == n && char_traits<value_type>::compare(pstr_, s, n1) == 0;
}

template <typename CharT>
bool pstring_ref<CharT>::equal(const basic_string<value_type>& s) const
{
    if (!pstr_)
        return s.empty();

    return pstr_ == s;
}

// pstring_out
template <typename CharT>
pstring_out<CharT>::pstring_out(basic_string<CharT>& s) :
    s_(s), val_(0)
{
    s_.clear();
}

template <typename CharT>
pstring_out<CharT>::~pstring_out()
{
    if (val_) {
        try {
            s_.assign(basic_string<CharT>::adopt(val_));
        } catch (const std::exception&) {
            memory::free(val_);
        }
    }
}

// pstring_out_size
template <typename CharT>
pstring_out_size<CharT>::pstring_out_size(basic_string<CharT>& s) :
    s_(s), val_(0), size_(0), length_(0)
{
    s_.clear();
}

template <typename CharT>
pstring_out_size<CharT>::~pstring_out_size()
{
    if (val_)
        memory::free(val_);
}

template <typename CharT>
void pstring_out_size<CharT>::commit()
{
    if (val_) {
        XPCOM_ASSERT(length_ <= size_);
        s_.assign(basic_string<CharT>::adopt(val_, length_));
        val_ = 0;
    }
}

// pstring_out_size2
template <typename CharT>
pstring_out_size2<CharT>::pstring_out_size2(basic_string<CharT>& s) :
    s_(s), val_(0), size_(0)
{
    s_.clear();
}

template <typename CharT>
pstring_out_size2<CharT>::~pstring_out_size2()
{
    if (val_)
        memory::free(val_);
}

template <typename CharT>
void pstring_out_size2<CharT>::commit()
{
    if (val_) {
        s_.assign(basic_string<CharT>::adopt(val_, size_));
        val_ = 0;
    }
}

// pstring_inout
template <typename CharT>
pstring_inout<CharT>::pstring_inout(basic_string<CharT>& s) :
    s_(s), val_(s.release_data())
{
}

template <typename CharT>
pstring_inout<CharT>::~pstring_inout()
{
    if (val_) {
        try {
            s_.assign(basic_string<CharT>::adopt(val_));
        } catch (const std::exception&) {
            memory::free(val_);
        }
    }
}

// pstring_inout_size
template <typename CharT>
pstring_inout_size<CharT>::pstring_inout_size(basic_string<CharT>& s) :
    s_(s)
{
    val_ = s_.release_data(&length_);
    size_ = length_;
}

template <typename CharT>
pstring_inout_size<CharT>::~pstring_inout_size()
{
    if (val_)
        memory::free(val_);
}

template <typename CharT>
void pstring_inout_size<CharT>::commit()
{
    if (val_) {
        XPCOM_ASSERT(length_ <= size_);
        s_.assign(basic_string<CharT>::adopt(val_, length_));
        val_ = 0;
    }
}

// pstring_inout_size2
template <typename CharT>
pstring_inout_size2<CharT>::pstring_inout_size2(basic_string<CharT>& s) :
    s_(s)
{
    val_ = s_.release_data(&size_);
}

template <typename CharT>
pstring_inout_size2<CharT>::~pstring_inout_size2()
{
    if (val_)
        memory::free(val_);
}

template <typename CharT>
void pstring_inout_size2<CharT>::commit()
{
    if (val_) {
        s_.assign(basic_string<CharT>::adopt(val_, size_));
        val_ = 0;
    }
}

// pstring_out_isize2
template <typename CharT>
pstring_out_isize2<CharT>::pstring_out_isize2(
        basic_string<CharT>& s, size_type size) :
    s_(s), val_(0), size_(size)
{
    s_.clear();
}

template <typename CharT>
pstring_out_isize2<CharT>::~pstring_out_isize2()
{
    if (val_) {
        try {
            // Ensure the returned size
            s_.assign(basic_string<CharT>::adopt(val_, size_));
        } catch (const std::exception&) {
            memory::free(val_);
        }
    }
}

// uuid_ptr_out
uuid_ptr_out::uuid_ptr_out(uuid& id) :
    id_(id), val_(0)
{
}

uuid_ptr_out::~uuid_ptr_out()
{
    if (val_) {
        id_ = *val_;
        memory::destroy(val_);
    } else {
        id_ = uuid::nil;
    }
}

// uuid_ptr_inout
uuid_ptr_inout::uuid_ptr_inout(uuid& id) :
    id_(id), val_(id.is_nil() ? 0 : ::new(mem) uuid(id))
{
}

uuid_ptr_inout::~uuid_ptr_inout()
{
    if (val_) {
        id_ = *val_;
        memory::destroy(val_);
    } else {
        id_ = uuid::nil;
    }
}

// Explicit instantiations
template class pstring_ref<char>;
template class pstring_ref<wchar>;

template class pstring_out<char>;
template class pstring_out<wchar>;
template class pstring_out_size<char>;
template class pstring_out_size<wchar>;
template class pstring_out_size2<char>;
template class pstring_out_size2<wchar>;
template class pstring_inout<char>;
template class pstring_inout<wchar>;
template class pstring_inout_size<char>;
template class pstring_inout_size<wchar>;
template class pstring_inout_size2<char>;
template class pstring_inout_size2<wchar>;
template class pstring_out_isize2<char>;
template class pstring_out_isize2<wchar>;

} // namespace xpcom::proxy
} // namespace xpcom
